import fs from "node:fs"; import path from "node:path"; import { spawn } from "node:child_process"; import { sendStream, setHeader } from "h3"; import { getExportTaskForUser, markExportTaskExpired } from "#server/service/export/jobs"; export default defineWrappedResponseHandler(async (event) => { const user = await event.context.auth.requireUser(); const idRaw = getRouterParam(event, "id"); const taskId = Number(idRaw); if (!Number.isInteger(taskId) || taskId < 1) { throw createError({ statusCode: 400, statusMessage: "无效的任务 id" }); } const task = await getExportTaskForUser(taskId, user.id); if (!task) { throw createError({ statusCode: 404, statusMessage: "导出任务不存在" }); } if (task.status !== "succeeded") { throw createError({ statusCode: 409, statusMessage: "导出任务尚未完成" }); } if (!task.expiresAt || task.expiresAt.getTime() <= Date.now()) { await markExportTaskExpired(task.id, "导出结果已过期,请重新导出"); throw createError({ statusCode: 410, statusMessage: "导出结果已过期" }); } if (!task.outputDir || !task.outputName) { await markExportTaskExpired(task.id, "导出结果缺失,请重新导出"); throw createError({ statusCode: 410, statusMessage: "导出文件已丢失,请重新导出" }); } const manifestPath = path.resolve(task.outputDir, "manifest.json"); if (!fs.existsSync(manifestPath)) { await markExportTaskExpired(task.id, "导出文件已丢失,请重新导出"); throw createError({ statusCode: 410, statusMessage: "导出文件已丢失,请重新导出" }); } const archiveName = `${task.outputName}.tar.gz`; const tarProc = spawn("tar", ["-czf", "-", "-C", task.outputDir, "."], { stdio: ["ignore", "pipe", "pipe"], }); tarProc.on("exit", async (code) => { if (code === 0) { return; } await markExportTaskExpired(task.id, "导出文件打包失败,请重新导出"); }); setHeader(event, "Content-Type", "application/gzip"); setHeader(event, "Content-Disposition", `attachment; filename="${archiveName}"`); return sendStream(event, tarProc.stdout); });